package com.elepant.utils;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * UTF-8【批量】转GBK文件工具类(转Oracle-SQL脚本)
 * 只用填个文件目录就行
 */
public class FileEnCodeUtilsTest {
    public void toUtf8() throws IOException {
        String path = "D:\\code\\abs-all\\abs-oracle-us1017900\\Wraith\\abs-common\\src\\main\\resources\\db\\config\\year";
        //先转换
        String replaceFilepathDir = replaceFileStr(path, StandardCharsets.UTF_8);
        //后切割
        // String pathToSplit = "E:\\workfile\\ind\\mysql\\todo2\\replace";
        //mysql脚本生成到 E:\workfile\ind\mysql\replace\文件夹下面
        //  int maxline = 200000; //按照20W一条切割
        //   SqlFileSplitUtils.split(replaceFilepathDir, maxline);
        //再执行
        //   String splitFileDir = "E:\\workfile\\ind\\mysql\\todo3\\replace\\split";
        //   SqlFileRunUtils.doExecuteBatchSql(splitFileDir);
    }

    public void toGbk2312() throws IOException {
        // String path = "D:\\code\\abs-all\\abs-oracle-us1017900\\Wraith\\abs-common\\src\\main\\resources\\db\\config\\year";
        // String path = "D:\\code\\amt-database\\database\\abs\\Oracle_sql\\increment_sql\\2.7.0\\cs_platform\\dml";
        // String path = "D:\\code\\amt-database\\database\\abs\\Oracle_sql\\increment_sql\\2.6.0\\cs_platform\\ddl\\seq";
        //String path = "D:\\code\\amt-database\\database\\abs\\Oracle_sql\\increment_sql\\2.6.0\\cs_platform\\dml";
        //String path = "D:\\code\\amt-database\\database\\abs\\Oracle_sql\\increment_sql\\2.8.0";
        String path = "D:\\code\\amt-database\\database\\abs\\Oracle_sql\\increment_sql\\2.9.0";
        //先转换 gb2312
        String replaceFilepathDir = replaceFileStr(path, Charset.forName("gb2312"));
    }

    //直接替换源文件,删除源文件 替换成新文件
    public void toGbk2312_all() {
        //
        // String path = "D:\\code\\abs-all\\abs-oracle-us1017900\\Wraith\\abs-common\\src\\main\\resources\\db\\config\\year";
        // String path = "D:\\code\\amt-database\\database\\abs\\Oracle_sql\\increment_sql\\2.7.0\\cs_platform\\dml";
        String path = "D:\\code\\amt-database\\database\\abs\\Oracle_sql\\increment_sql\\2.6.0\\cs_platform\\ddl\\view";
        //先转换 gb2312

        String replaceFilepathDir = replaceFileStr(path, Charset.forName("gb2312"));
    }

    //递归操作
    public static String replaceFileStr(String path, Charset targetCharset) {
        String replaceFilepathDir = path + "\\" + "replace";
        File replaceFilepathDirFile = new File(replaceFilepathDir);

        //遍历文件夹内所有内容，不包换文件夹里的文件夹里的内容
        //获取其file对象
        File file = new File(path);
        //遍历path下的文件和目录，放在File数组中
        File[] fileArray = file.listFiles();
        //判断文件夹内是否有文件
        if (fileArray != null) {
            //遍历File[]数组
            for (File f : fileArray) {
                if (f.isDirectory()) {
                    // 跳过 replaceFilepathDir
                    if (f.getAbsolutePath().equals(replaceFilepathDir) ) {
                        System.err.println("当前file是replace文件夹,跳过:" + f.getPath());
                        continue;
                    }
                    if (f.listFiles() == null || f.listFiles().length == 0) {
                        System.err.println("当前file是文件夹,并且是空文件夹跳过:" + f.getPath());
                        continue;
                    }
                    //递归执行
                    System.out.println("当前file是文件夹,继续递归:" + f.getPath());
                    replaceFileStr(f.getPath(), targetCharset);
                }

                String filepath = f.getPath();
                String parentFile = f.getParent();

                int idotIndex = f.getName().indexOf(".");
                if (idotIndex < 0) {
                    System.out.println("当前file文件类型不确定跳过:" + f.getPath());
                    continue;
                }
                String fileNamePrefix = f.getName().substring(0, idotIndex);
                String fileNameSuffix = f.getName().substring(idotIndex);

                //要替换的旧内容,写完将sb 清空
                // String fileNameNew = fileNamePrefix + "_"+targetCharset.toString() + fileNameSuffix;
                String fileNameNew = fileNamePrefix + fileNameSuffix;
                String replaceFilepath = parentFile + "\\" + "replace" + "\\" + fileNameNew;
                FileInputStream is = null;
                InputStreamReader isr = null;
                OutputStream os = null;
                OutputStreamWriter osw = null;
                BufferedWriter bw = null;
                //原文件编码格式
                Charset sourceCharset = null;
                try {
                    // 创建文件输入流
                    is = new FileInputStream(filepath);
                    boolean isUtf8Flag = isUtf8(is);
                    System.out.println("当前file文件编码类型是否是UTF8:" + isUtf8Flag);
                    if (isUtf8Flag) {
                        sourceCharset = StandardCharsets.UTF_8;
                    } else {
                        //当做gbk处理
                        sourceCharset = Charset.forName("gb2312");
                    }
                    //如果 源文件编码格式跟要转换的一致直接跳过
                    if (sourceCharset.compareTo(targetCharset) == 0) {
                        System.err.println("源文件编码格式跟要转换的一致直接跳过:" + filepath);
                        continue;
                    }

                    boolean replaceFilepathExistFlag = replaceFilepathDirFile.exists();
                    if (!replaceFilepathExistFlag) {
                        replaceFilepathDirFile.mkdir();
                    }

                    isr = new InputStreamReader(new FileInputStream(filepath), sourceCharset);
                    StringBuilder sb = new StringBuilder();
                    // fis.read(data)：将字符读入数组。在某个输入可用、发生I/O错误或者已到达流的末尾前，此方法一直阻塞。
                    // 读取的字符数，如果已到达流的末尾，则返回 -1
                    int rn = 0;
                    //  优化,,,如果字符串过大需要分批写入
                    // 创建文件输出流
                    //FileWriter replaceFileWriter = new FileWriter(replaceFilepath);
                    File fileNew = new File(replaceFilepath);
                    //判断文件或文件夹是否存在, //文件不存在就要新建文件  //文件存在就要删除文件
                    boolean flag = fileNew.exists();
                    if (flag) {
                        fileNew.delete();
                    } else {
                        fileNew.createNewFile();
                    }
                    os = new FileOutputStream(fileNew);
                    osw = new OutputStreamWriter(os, targetCharset);
                    bw = new BufferedWriter(osw);
                    int lineNo = 0;
                    int len = 0;
                    while ((len = isr.read()) != -1) {
                        //4.使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
                        osw.write(len);
                    }
                    // 关闭文件流，释放资源
                    isr.close();
                    bw.close();
                    //若非目录(即文件)，则打印，提示替换完成
                    if (!f.isDirectory()) {
                        System.out.println(f.getPath() + "已完成替换。");
                    }


                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else {
            System.out.println("文件夹为空！");
        }
        return replaceFilepathDir;
    }


    /**
     * 判断文件内容是否为 UTF-8 编码
     * 翻译自C++版本 @see <a href="https://blog.csdn.net/jiangqin115/article/details/42684017">cpp原文</a>
     *
     * @author Piao
     */
    public static boolean isUtf8(FileInputStream fis) {
        //请注意FileInputStream是流，是不能复用的！
        try {
            while (true) {
                int curr = fis.read();
                //System.out.println(curr);
                if (curr == -1) {
                    return true;
                }
                if (curr < 0x80) {// (10000000): 值小于0x80的为ASCII字符
                } else if (curr < (0xC0)) { // (11000000): 值介于0x80与0xC0之间的为无效UTF-8字符
                    return false;
                } else if (curr < (0xE0)) { // (11100000): 此范围内为2字节UTF-8字符
                    if ((fis.read() & (0xC0)) != 0x80) {
                        return false;
                    }
                } else if (curr < (0xF0)) { // (11110000): 此范围内为3字节UTF-8字符
                    if ((fis.read() & (0xC0)) != 0x80 || (fis.read() & (0xC0)) != 0x80) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return true;
        }
    }
}
